package com.eu.mybatis.helper;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Assert;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import com.eu.mybatis.config.PartitionConfig;
import com.eu.mybatis.partition.strategy.PartitionStrategy;

public class PartitionConfigHelper {

	private static Logger logger = LoggerFactory.getLogger(PartitionConfigHelper.class);

	private static final String CLASS = "class";

	private static final String TABLE = "table";

	private static final String STRATEGY = "strategy";

	private static final Map<String, String> DOC_TYPE_MAP = new HashMap<String, String>();

	static {
		DOC_TYPE_MAP.put("http://mybatis.eu.com/dtd/partition-config.dtd", "com/eu/mybatis/partition/partition-config.dtd");
	}

	/**
	 * 解析分表配置文件：partition-config.xml
	 */
	public static void initPartitionConfig() {
		InputStream input = null;
		try {
			input = new ClassPathResource("partition-config.xml").getInputStream();
			parse(input);
		} catch (FileNotFoundException e) {
			logger.info("不存在分表配置：{}", "partition-config.xml");
		} catch (Throwable e) {
			throw new IllegalArgumentException("分表配置解析失败：partition-config.xml", e);
		} finally {
			IOUtils.closeQuietly(input);
		}
	}

	private static void parse(InputStream input) throws Exception {
		SAXParserFactory spf = SAXParserFactory.newInstance();
		spf.setValidating(true);
		spf.setNamespaceAware(true);
		SAXParser parser = spf.newSAXParser();
		XMLReader reader = parser.getXMLReader();

		DefaultHandler handler = new DefaultHandler() {

			public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
				
				/**
				 * 解析<strategy />标签
				 */
				if (STRATEGY.equals(qName)) {
					
					String tableName = attributes.getValue(TABLE);
					Assert.hasLength(tableName, "分表配置信息有误：strategy >> table属性不能为空");
					
					String className = attributes.getValue(CLASS);
					Assert.hasLength(className, "分表配置信息有误：strategy >> class属性不能为空");
					
					try {
						if (PartitionConfig.getStrategy(tableName) == null) {
							PartitionStrategy strategy = (PartitionStrategy) Class.forName(className).newInstance();
							PartitionConfig.addStrategy(tableName, strategy);
							PartitionConfig.addTableName(tableName);
						}
					} catch (Exception e) {
						throw new IllegalArgumentException("初始化分表配置异常", e);
					}
				}
			}

			public void error(SAXParseException e) throws SAXException {
				throw e;
			}

			public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException {
				try {
					if (systemId != null) {
						return new InputSource(new ClassPathResource(PartitionConfigHelper.DOC_TYPE_MAP.get(systemId)).getInputStream());
					} else if (publicId != null) {
						return new InputSource(new ClassPathResource(PartitionConfigHelper.DOC_TYPE_MAP.get(publicId)).getInputStream());
					}
					return null;
				} catch (Exception e) {
					throw new SAXException(e);
				}
			}

		};

		reader.setContentHandler(handler);
		reader.setEntityResolver(handler);
		reader.setErrorHandler(handler);
		reader.parse(new InputSource(input));
	}

}